﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

public class JsonTools
{

    public class JsonNode
    {
        public bool IsArray
        {
            get { return array != null; }
        }

        public bool IsObject
        {
            get { return map != null; }
        }
        
        public JsonNode parent = null;
        public JsonNode root
        {
            get
            {
                JsonNode r = this;
                while (r.parent != null) 
                    r = r.parent;
                return r.parent;
            }
        }
        public string value = null;
        public Dictionary<string, JsonNode> map;
        public List<JsonNode> array;

        public JsonNode()
        {

        }

        public JsonNode(string value)
        {
            this.value=value;
        }

        public override string ToString()
        {
            if (map != null) return "object:"+map.ToString();
            if (array != null) return "array:"+array.ToString();
            if (value != null) return value;
            return "(null)";
        }

        public JsonNode find(string path)
        {
            if (path == null || path.Length == 0) return null;
            int i = 0;
            while (i < path.Length && path[i] != '/' && path[i] != '[') i++;
            if (i == 0)
            {
                if (path[0] == '/')
                {
                    if (path.Length > 1)
                        return (root ?? this).find(path.Substring(1));
                    else
                        return (root ?? this);
                }
                else if (IsArray) // =='['
                {
                    int j = path.IndexOf(']', i);
                    int idx;
                    if (j > i && int.TryParse(path.Substring(i + 1, j - i - 1), out idx))
                    {
                        if (idx < 0) idx += array.Count;
                        if (idx >= 0 && idx < array.Count && array[idx] != null)
                        {
                            if (j + 1 < path.Length)
                                return array[idx].find(path.Substring(j + 1));
                            else
                                return array[idx];
                        }
                    }
                }
                return null;
            }
            else if (i < path.Length)  // found
            {
                string name = path.Substring(0, i);
                if (path[i] == '/')
                {
                    if (name == ".")
                    {
                        if (i + 2 < path.Length)
                            this.find(path.Substring(i + 1));
                        else
                            return this;
                    }
                    else if (name == "..")
                    {
                        if (i + 3 < path.Length)
                        {
                            if (parent != null)
                                parent.find(path.Substring(i + 1));
                        }
                        else
                            return parent;
                    }
                }
                if (IsObject && map.ContainsKey(name))
                {
                    if (i + 1 < path.Length)
                    {
                        if (map[name] != null)
                            return map[name].find(path.Substring(i));
                    }
                    else
                        return map[name];
                }
                return null;
            }
            else  // not found
            {
                if (map.ContainsKey(path))
                    return map[path];
                return null;
            }
        }
    }

    public static JsonNode ParseJson(string json)
    {
        if (string.IsNullOrEmpty(json)) return null;
        json=json.Trim();
        return ParseJson(json, 0, json.Length);
    }

    static int findMatchChar(string json, int i, int j,char c)
    {
        int count1=0,count2=0;
        bool mark=true;
        while(i<j&&(count1!=0||count2!=0||!mark||json[i]!=c))
        {
            if(mark)
            {
                if(json[i]=='"' && (i<1 || json[i-1]!='\\')) 
                    mark=false;
                else if(json[i]=='{') count1++;
                else if(json[i]=='}') count1--;
                else if(json[i]=='[') count2++;
                else if(json[i]==']') count2--;
            } else if(json[i]=='"'&&json[i-1]!='\\') 
                mark=true;
            i++;
        }
        if(i<j) return i;
        return  -1;
    }


    static JsonNode ParseJson(string json, int i, int j, JsonNode parent = null)
    {
        if(i>=j) return null;
        while(i<j&&json[i]<=' ') i++;
        while(i<j&&json[j-1]<=' ') j--;
        JsonNode node = new JsonNode();
        if (json[i] == '{')  //对象
        {
            node.map=new Dictionary<string,JsonNode>();
            i++;
            j = findMatchChar(json, i, j,'}');
            if(i<j)
            {
                int k=findMatchChar(json,i,j,','),m,n;
                while(k>i)
                {
                   
                    m=findMatchChar(json,i,k,':');
                    if(m>i) //found key
                    {
                        while (i < j && json[i] <= ' ') i++;
                        if (json[i] == '"') i++;
                        n = m-1;
                        while (i < n && json[n] <= ' ') n--;
                        if (json[n] == '"') n--;
                        if (i < n)
                        {
                            string key = json.Substring(i, n-i+1);
                            JsonNode value = ParseJson(json, m + 1, k);
                            if (value != null)
                                value.parent = parent;
                            node.map.Add(key, value);
                            i = k + 1;
                            k = findMatchChar(json, i, j, ',');
                        }
                        else
                            throw new Exception("No key found ':',offset:" + i);
                    }     
                    else 
                        throw new Exception("No match ':',offset:" + i);
                }
                //find end value
                m=findMatchChar(json,i,j,':');
                if(m>i) //found value
                {
                    while (i < j && json[i] <= ' ') i++;
                    if (json[i] == '"') i++;
                    n = m-1;
                    while (i < n && json[n] <= ' ') n--;
                    if (json[n] == '"') n--;
                    if (i < n)
                    {
                        string key = json.Substring(i, n-i+1);
                        JsonNode value = ParseJson(json, m + 1, j);
                        if (value != null)
                            value.parent = parent;
                        node.map.Add(key, value);
                    }
                    else
                        throw new Exception("No key found ':',offset:" + i);
                }
                else
                    throw new Exception("No key found ':',offset:" + i);
                return node;
            }
            else
                throw new Exception("No match '}',offset:" + i);
        }
        else if (json[i] == '[') //数组
        {
            node.array = new List<JsonNode>();
            i++;
            j = findMatchChar(json, i, j, ']');
            if (i < j)
            {
                JsonNode value;
                int k = findMatchChar(json, i, j, ',');
                while (k > i)
                {
                    value = ParseJson(json, i, k);
                    if(value!=null)
                        value.parent = parent;
                    node.array.Add(value);
                    i = k + 1;
                    k = findMatchChar(json, i, j, ',');
                }
                //find end value
                value = ParseJson(json, i, j);
                if (value != null)
                    node.array.Add( value);
                return node;
            }
            else
                throw new Exception("No match '}',offset:" + i);
        }
        else if(json[i]=='"')//字符串
        {
            if(json[j-1]=='"')
                j--;
            else
                throw new Exception("No match '\"',offset:" + i);
            i++;
            node.value=json.Substring(i,j-i);
            return node;
        }
        else  //or 数字
        {
            node.value = json.Substring(i , j - i);
            if (node.value == "NULL") node.value = null;
            return node;
        }
    }

    public static string Serialize(Object obj)
    {
        StringBuilder sb = new StringBuilder();
        Serialize(obj, sb);
        return sb.ToString();
    }

    public static T[] DeserializeArray<T>(string json)
    {
        JsonNode node = ParseJson(json);
        if (node == null) return new T[0];
        if (node.array == null) 
            throw new Exception("The input json string is not a array!");
        if (node.map == null || node.map.Count == 0) return new T[0];
        var vs=node.map.Values;
        T[] tmp=new T[vs.Count];
        int i=0;
        foreach(var v in vs)
        {
            T t = Activator.CreateInstance<T>();
            Deserialize<T>(ref t,v);
            tmp[i]=t;
            i++;
        }
        return tmp;
    }

    public static T Deserialize<T>(string json)
    {
        JsonNode node = ParseJson(json);
        if (node == null) return default(T);
        if (node.IsArray) throw new Exception("Error:the input json string is a array!");
        T tmp = Activator.CreateInstance<T>();
        Deserialize<T>(ref tmp, node);
        return tmp;       
    }

    public static void Deserialize<T>(ref T tmp, JsonNode node)
    {
        Type t = tmp.GetType();
        if (t.IsClass || (t.IsValueType && !t.IsEnum && t.IsPrimitive))  //类或者结构体
        {
            FieldInfo[] fis = t.GetFields();
            foreach (var fi in fis)
                if (!fi.IsNotSerialized && node.map != null && node.map.ContainsKey(fi.Name))
                {
                    JsonNode n = node.map[fi.Name];
                    if (fi.FieldType.IsValueType)
                    {
                        if (fi.FieldType.IsValueType)
                        {
                            if (fi.FieldType.IsEnum)
                            {
                                if (!string.IsNullOrEmpty(n.value))
                                {
                                    if (n.value[0] == '\"')
                                        fi.SetValue(tmp, Enum.Parse(fi.FieldType, n.value.Substring(1, n.value.Length - 2)));
                                    else
                                        fi.SetValue(tmp, Enum.Parse(fi.FieldType, n.value));
                                }
                            }
                            else if (fi.FieldType.IsPrimitive)
                            {
                                if (fi.FieldType == typeof(int))
                                    fi.SetValue(tmp, int.Parse(n.value));
                                else if (fi.FieldType == typeof(bool))
                                    fi.SetValue(tmp, bool.Parse(n.value));
                                else if (fi.FieldType == typeof(float))
                                    fi.SetValue(tmp, float.Parse(n.value));
                                else if (fi.FieldType == typeof(double))
                                    fi.SetValue(tmp, double.Parse(n.value));
                                else if (fi.FieldType == typeof(decimal))
                                    fi.SetValue(tmp, decimal.Parse(n.value));
                                else if (fi.FieldType == typeof(uint))
                                    fi.SetValue(tmp, uint.Parse(n.value));
                                else if (fi.FieldType == typeof(long))
                                    fi.SetValue(tmp, long.Parse(n.value));
                                else if (fi.FieldType == typeof(ulong))
                                    fi.SetValue(tmp, ulong.Parse(n.value));
                                else if (fi.FieldType == typeof(short))
                                    fi.SetValue(tmp, short.Parse(n.value));
                                else if (fi.FieldType == typeof(ushort))
                                    fi.SetValue(tmp, ushort.Parse(n.value));
                                else if (fi.FieldType == typeof(byte))
                                    fi.SetValue(tmp, byte.Parse(n.value));
                                else if (fi.FieldType == typeof(sbyte))
                                    fi.SetValue(tmp, sbyte.Parse(n.value));
                                else if (fi.FieldType == typeof(char))
                                    fi.SetValue(tmp, char.Parse(n.value));
                            }
                        }
                        else  //结构体
                        {
                            var s = Activator.CreateInstance(fi.FieldType);
                            Deserialize(ref s, n);
                            fi.SetValue(tmp, s);
                        }
                    }
                    else if (fi.FieldType == typeof(string))
                    {
                        if (n.value == null || n.value == "null")
                            fi.SetValue(tmp, null);
                        else
                            fi.SetValue(tmp, n.value);
                    }
                    else if (fi.FieldType.IsArray&&fi.FieldType.GetArrayRank()==1)
                    {
                        if (n.map != null)
                        {
                            var nn = n.map.Values;
                            Type nt = fi.FieldType.GetElementType();
                            Array array = Array.CreateInstance(nt, nn.Count);

                            int i = 0;
                            foreach (var nnn in nn)
                            {
                                if (nnn != null)
                                {
                                    object o = Activator.CreateInstance(nt);
                                    Deserialize(ref o, nnn);
                                    array.SetValue(o, i);
                                }
                                i++;
                            }
                            fi.SetValue(tmp, array);
                        }
                    }
                }
        }   //class or struct
    }

    public static string Serialize(List<Object> objs)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append('[');
        if (objs.Count > 0)
        {
            //第一个元素
            Serialize(objs[0], sb);
            for (int i = 1; i < objs.Count; i++) //剩下的元素
            {
                sb.Append(',');
                Serialize(objs[i], sb);
            }
        }
        sb.Append(']');
        return sb.ToString();
    }

    public static string Serialize(Dictionary<Object, Object> objs)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append('[');
        var keys = objs.Keys;
        bool first = true;
        foreach (var key in keys)
        {
            if (first)
            {
                first = false;
                sb.Append("{\"key\":");
            }
            else
                sb.Append(",{\"key\":");
            Serialize(key, sb);
            sb.Append(",\"value\":");
            Serialize(objs[key], sb);
            sb.Append('}');
        }
        sb.Append(']');
        return sb.ToString();
    }

    static void Serialize(Object obj, StringBuilder sb)
    {
        Type t = obj.GetType();
        if (t.IsValueType)  //值类型
        {
            if (t.IsPrimitive)  //基本类型
                sb.Append(obj.ToString());
            else if (t.IsEnum)  //枚举类型
            {
                sb.Append(((Enum)obj).ToString());
            }
            else  //结构体
            {
                sb.Append('{');
                bool first = true;
                FieldInfo[] fis = t.GetFields();
                foreach (var fi in fis)
                    if (!fi.IsNotSerialized)
                    {
                        if (!first) sb.Append(',');
                        sb.AppendFormat("\"{0}\":", fi.Name);
                        object o = fi.GetValue(obj);
                        if (o == null)
                            sb.Append("null");
                        else
                            Serialize(o, sb);
                        if (first) first = false;
                    }
                sb.Append('}');
            }
        }
        else if (obj is string)
        {
            sb.Append('\"');
            sb.Append((string)obj);
            sb.Append('\"');
        }
        else if (t.IsArray&&t.GetArrayRank()==1)   //数组类型
        {
            Array objs = (Array)obj;
            sb.Append('[');
            bool first = true;
            foreach (var o in objs)
            {
                if (!first) sb.Append(',');
                if (o == null)
                    sb.Append("null");
                else
                    Serialize(o, sb);
                if (first) first = false;
            }
            sb.Append(']');
        }
        else if (obj is ArrayList)
        {
            ArrayList objs = (ArrayList)obj;
            sb.Append('[');
            bool first = true;
            foreach (var o in objs)
            {
                if (!first) sb.Append(',');
                if (o == null)
                    sb.Append("null");
                else
                    Serialize(o, sb);
                if (first) first = false;
            }
            sb.Append(']');
        }
        else if (obj is Hashtable)
        {
            Hashtable objs = (Hashtable)obj;
            var keys = objs.Keys;
            bool first = true;
            sb.Append('[');
            foreach (var key in keys)
            {
                if (first)
                {
                    first = false;
                    sb.Append("{\"key\":");
                }
                else
                    sb.Append(",{\"key\":");
                Serialize(key, sb);
                sb.Append(",value:");
                Object o = objs[key];
                if (o == null)
                    sb.Append("null");
                else
                    Serialize(o, sb);
                sb.Append('}');
            }
            sb.Append(']');
        }
        else if (t.IsClass)//
        {
            sb.Append('{');
            bool first = true;
            FieldInfo[] fis = t.GetFields();
            foreach (var fi in fis)
                if (!fi.IsNotSerialized)
                {
                    if (!first) sb.Append(',');
                    sb.AppendFormat("\"{0}\":", fi.Name);
                    object o = fi.GetValue(obj);
                    if (o == null)
                        sb.Append("null");
                    else
                        Serialize(o, sb);
                    if (first) first = false;
                }
            sb.Append('}');
        }
    }

}
